/**
 * \file stream.h
 * \brief A generic stream object. Can be attached to a file or to
 * a binary chunk.
 **/

#ifndef RIPE_STREAM_H
#define RIPE_STREAM_H

#include <stdio.h>
#include "standard.h"
#include "chunk.h"

enum RipeStreamTypeT {
  RS_STREAM_FILE,  /* This stream is attached to a FILE* */
  RS_STREAM_CHUNK, /* This stream is attached to a Chunk */
};
typedef enum RipeStreamTypeT RipeStreamType;

struct RipeStreamT {
  RipeStreamType type;
  uint32_t cursor; /* Offset from the start of the stream (if applicable) */
  union {
    struct {
      FILE* file;
    } stream_file;
    struct {
      Chunk* chunk;
    } stream_chunk;
  };
};
typedef struct RipeStreamT RipeStream;

/**
 * \brief When seeking through a RipeStream, where to start from.
 **/
enum RipeStreamStartT {
  RS_STREAM_START, /* Beginning of the stream. */
  RS_STREAM_END, /* End of the stream. */
  RS_STREAM_CURRENT, /* Current position in the stream. */
};
typedef enum RipeStreamStartT RipeStreamStart;

/**
 * Streams should be able to live on the heap and on the stack. After
 * allocating a stream somewhere, make sure to initialize type, cursor and
 * stream_? fields! Try to always open the files in binary mode.
 **/

/**
 * \brief Have we reached the end of the stream?
 **/
bool rs_stream_end(RipeStream* stream);

/**
 * \brief Read #n bytes into #buffer. 
 * \return Number of bytes read.
 **/
uint32_t rs_stream_read(RipeStream* stream, uint32_t n, void* buffer);

/**
 * \brief Seek to a position in the stream (if possible).
 **/
int rs_stream_seek(RipeStream* stream, RipeStreamStart start, int32_t n);

/**
 * \brief Return the current position in the stream (if defined).
 **/
uint32_t rs_stream_tell(RipeStream* stream);

/**
 * \brief Write #n bytes from #buffer into the stream.
 * \return Number of bytes written.
 **/
uint32_t rs_stream_write(RipeStream* stream, uint32_t n, void* buffer);

#endif /* RIPE_STREAM_H */
